#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cassert>
#include <windows.h>

using namespace std;

bool createdirectoryrecursive(const string& path)//使用递归创建多级目录
{
	string currentpath;//当前的路径
	size_t pos = 0;//当前路径的分隔符位置
	while ((pos = path.find_first_of("\\/", pos + 1)) != string::npos)//查找路径中的分隔符，来创建目录
	{
		currentpath = path.substr(0, pos);//获取当前路径
		if (!CreateDirectoryA(currentpath.c_str(), NULL))//创建目录
		{
			if (GetLastError() != ERROR_ALREADY_EXISTS)//如果目录已经存在，就不报错
			{
				cerr << "failed to create directory: " << currentpath << endl;
				return false;
			}
		}
	}
	if (!path.empty() && !CreateDirectoryA(path.c_str(), NULL))//创建最后一级目录
	{
		if (GetLastError() != ERROR_ALREADY_EXISTS)
		{
			cerr << "failed to create directory: " << path << endl;
			return false;
		}
	}
	return true;
}
vector<unsigned char> compressrle(const vector<unsigned char>& data)//使用RLE算法压缩数据，把重复的字符压缩成一个字符和一个计数
{
	vector<unsigned char> result;
	if (data.empty()) {
		return result;//如果数据为空，直接返回空
	}
	unsigned char current = data[0];//当前处理的字符
	int count = 1;//当前字符的计数
	for (size_t i = 1; i < data.size(); ++i) //遍历数据，统计重复的字符
	{
		if (data[i] == current&&count<255) //unsigned char的最大值为255
		{
			count++;
		}
		else {
			result.push_back(count); //把计数和字符压入结果
			result.push_back(current);
			current = data[i]; //更新当前字符
			count = 1; //重置计数
		}
	}
	result.push_back(count); //把最后一个字符和计数压入结果
	result.push_back(current);
	return result;
}

vector<unsigned char> decompressrle(const vector<unsigned char>& compressed)//使用算法，把压缩的数据还原成原始数据
{
	vector<unsigned char> result;
	for (size_t i = 0; i < compressed.size(); i += 2) //遍历压缩数据，每两个字节为一组，第一个字节为计数，第二个字节为字符
	{
		if (i + 1 >= compressed.size()) //确定有足够的字节数
		{
			break;
		}
		unsigned char count = compressed[i];//重复的次数
		unsigned char character = compressed[i + 1];//重复的字节
		result.insert(result.end(), count, character); //把字符插入到结果中，重复count次
	}
	return result;
}

bool parseArgs(int argc, char* argv[], bool& iscompress,bool&recursive, vector<string>& inputfiles, string& outputdir)//解析命令行的参数
{
	iscompress = true;//默认压缩
	recursive = false;//默认不递归
	outputdir = "";//默认输出目录为程序所在的目录
	for (int i = 1;i < argc;++i)//遍历所有的参数，第一个是程序的名称，从第二个参数开始
	{
		string arg = argv[i];
		if (arg == "-c" || arg == "--compress")//设置程序的模式，压缩或解压缩，是否递归
		{
			iscompress = true;
		}
		else if (arg == "-d" || arg == "--decompress")
		{
			iscompress = false;
		}
		else if (arg == "-r" || arg == "--recursive")
		{
			recursive = true;
		}
		else if (arg == "-o" || arg == "--output")//如果指定目录
		{
			if (i + 1 < argc)
			{
				outputdir = argv[++i];//读取下一个参数作为输出目录
			}
			else
			{
				cerr << "first wrong" << endl;//没有指定输出目录，就报错
				return false;
			}
		}
		else//输入文件或是目录的参数
		{
			inputfiles.push_back(arg);//把参数加入到输入文件列表中
		}
	}
	if (inputfiles.empty())//检查输入的文件或目录是否为空
	{
		return false;
	}
	return true;
}
bool createmultileveldirectory(const string& path)//创建多级目录，按照单个字符来处理，方便连续的路径
{
	string temppath;//临时的路径
	for (size_t i = 0;i < path.length();++i)//按照字符逐个构建路径，遇到\的时候创建目录
	{
		temppath += path[i];
		if (path[i] == '\\')//\\是windows的路径分隔符
		{
			if (!CreateDirectoryA(temppath.c_str(), NULL))
			{
				if (GetLastError() != ERROR_ALREADY_EXISTS)
				{
					cerr << "failed to create directory: " << temppath << endl;
					return false;
				}
			}
		}
	}
	return true;
}
bool processsinglefile(const string& inputfile, const string& outputdir, bool iscompress)//处理单个文件，压缩或解压缩
{
	string outputfile;//输出文件的路径
	string filename = inputfile.substr(inputfile.find_last_of("/\\") + 1);//获取文件名
	if (iscompress)//生成输出时的文件名
	{
		outputfile = outputdir.empty() ? filename + ".rle" : outputdir + "/" + filename + ".rle";//在文件后面加上.rle后缀，方便区分
	}
	else
	{
		if (filename.size() > 4 && filename.substr(filename.size() - 4)==  ".rle")//确保解压缩的是rle压缩文件
		{
			outputfile = outputdir.empty() ? filename.substr(0, filename.size() - 4) : outputdir + "/" + filename.substr(0, filename.size() - 4);//去除.rle后缀
		}
		else
		{
			cerr << "second wrong" << endl;
			outputfile = outputdir.empty() ? filename + ".decoded" : outputdir + "/" + filename + ".decoded";
		}
	}
	try
	{
		if (iscompress)//是进行压缩时
		{
			ifstream in(inputfile, ios::binary);//用二进制方式打开文件
			if (!in)
			{
				cerr << "third wrong" << inputfile << endl;
				return false;
			}
			vector<unsigned char> data((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());//读取文件的内容，存入到vector中
			in.close();
			if (data.empty())
			{
				cerr << "forth wrong" << endl;//如果文件为空，直接返回
				return true;
			}
			vector<unsigned char>compreessed = compressrle(data);//压缩数据
			size_t lastslash = outputfile.find_last_of("\\/");//创建输出目录
			if (lastslash != string::npos)
			{
				string parentsdirpart = outputfile.substr(0, lastslash);
				if (!createmultileveldirectory(parentsdirpart.c_str()))
				{
					cerr << "14 wrong" << parentsdirpart << endl;
					return false;
				}
			}
			ofstream out(outputfile, ios::binary);//用二进制方式打开输出文件，写入数据
			if(!out)
			{
				cerr << "fifth wrong" << outputfile << endl;
				return false;
			}
			out.write(reinterpret_cast<const char*>(compreessed.data()), compreessed.size());
			out.close();
			cout << "success" << endl << inputfile << " to " << outputfile << endl;
			double ratio = 100.0 * compreessed.size() / data.size();
			cout << "ratio=" << ratio << "%" << endl;//成功压缩后，输出压缩率
		}
		else//解压缩
		{
			ifstream in(inputfile, ios::binary);
			if(!in)
			{
				cerr << "sixth wrong" << inputfile << endl;
				return false;
			}
			vector<unsigned char> compressed((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());//读取压缩文件的内容
			in.close();
			if (compressed.empty())
			{
				cerr << "seventh wrong" << endl;
				return true;
			}
			vector<unsigned char> decompressed = decompressrle(compressed);//解压缩数据
			size_t lastslash = outputfile.find_last_of("\\/");//创建输出文件的目录
			if (lastslash != string::npos)
			{
				string parentsdir = outputfile.substr(0, lastslash);
				if (!createdirectoryrecursive(parentsdir))
				{
					cerr << "15 wrong" << parentsdir << endl;
					return false;
				}
			}
			ofstream out(outputfile, ios::binary);//用二进制方式打开输出文件，写入数据
			if (!out)
			{
				cerr << "eighth wrong" << outputfile << endl;
				return false;
			}
			out.write(reinterpret_cast<const char*>(decompressed.data()), decompressed.size());
			out.close();
			cout << "success" << endl << inputfile << " to " << outputfile << endl;
		}
	}
	catch (const exception)//如果出现异常，结束程序
	{
		cerr << "ninth wrong" << inputfile << endl;
		return false;
	}
	return true;
}
void traversedirectory(const string& dirpath, vector<string>& filelist, bool iscompress)//递归遍历目录，获取目录下的所有文件
{
	WIN32_FIND_DATAA findData;//文件查找数据结构
	string searchPath = dirpath + "\\*";//在目录后面加上*，表示查找所有文件
	HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);//查找第一个文件
	if (hFind == INVALID_HANDLE_VALUE)//出现错误，无法打开目录
	{
		cerr << "tenth wrong" << dirpath << endl;
		return;
	}
	do//遍历目录中的所有条目
	{
		if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0)//如果是当前目录或上级目录，就跳过
		{
			continue;
		}
		string fullPath = dirpath + "\\" + findData.cFileName;//获取文件的完整路径
		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			traversedirectory(fullPath, filelist, iscompress);
		}
		else//如果是文件就添加到文件列表中
		{
			filelist.push_back(fullPath);
		}
	} while (FindNextFileA(hFind, &findData));//继续查找下一个文件
	FindClose(hFind);//关闭查找句柄
}
bool processfileordirectory(const string& inputpath, const string& outputdir, bool iscompress, bool recursive)//处理文件或目录，压缩或解压缩
{
	DWORD fileattr = GetFileAttributesA(inputpath.c_str());//获取文件或目录的属性
	if (fileattr == INVALID_FILE_ATTRIBUTES)
	{
		cerr << "11 wrong" << endl;
		return false;
	}
	if (fileattr & FILE_ATTRIBUTE_DIRECTORY)//如果是目录，就遍历目录
	{
		vector<string> filelist;
		if (recursive)
		{
			traversedirectory(inputpath, filelist, iscompress);
		}
		else //如果不是递归，就只处理当前目录下的文件
		{
			WIN32_FIND_DATAA findData;
			string searchpath = inputpath + "\\*";
			HANDLE hFind = FindFirstFileA(searchpath.c_str(), &findData);
			if (hFind == INVALID_HANDLE_VALUE)
			{
				cerr << "cant find directory - " << inputpath << endl;
				return false;
			}
			do
			{
				if (strcmp(findData.cFileName, ".") == 0 || strcmp(findData.cFileName, "..") == 0)
				{
					continue;
				}
				string fullpath = inputpath + "\\" + findData.cFileName;//仅仅获取当前目录下的文件，而不是递归
				if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
				{
					filelist.push_back(fullpath);
				}
			} while (FindNextFileA(hFind, &findData));
			FindClose(hFind);
		}
		string dirname = inputpath.substr(inputpath.find_last_of("\\/") + 1);//获取目录的名称
		string outputsubdir = outputdir.empty() ? dirname : outputdir + "\\" + dirname;//输出目录
		if (filelist.empty())
		{
			cerr << "12 wrong" << inputpath << endl;
			return true;
		}
		for (const string& file : filelist)//处理目录下的每个文件
		{
			string relativepath = file.substr(inputpath.length() + 1); //获取相对路径，并去掉输入目录的部分
			for (char& c : relativepath)
			{
				if (c == '/')
				{
					c = '\\';
				}
			}
			string outputfile = outputsubdir + "\\" + relativepath;//输出文件的路径
			size_t lastslash = outputfile.find_last_of("\\");
			string outputdirpart = (lastslash != string::npos) ? outputfile.substr(0, lastslash) : outputsubdir;
			if (!createmultileveldirectory(outputdirpart))//创建输出目录
			{
				cerr << "wrong cant found dirctory - " << outputdirpart << endl;
				continue;
			}
			if (!processsinglefile(file, outputdirpart, iscompress))//处理单个的文件
			{
				cerr << "wrong cant deal dirctory - " << file << endl;
			}
		}
		return true;
	}
	else//如果是文件，就直接处理
	{
		return processsinglefile(inputpath, outputdir, iscompress);
	}
}
int main(int argc, char* argv[])
{
	bool iscompress = true;//默认压缩
	bool recursive = false;//默认不递归
	string outputdir;//输出目录
	vector<string> inputfiles;//输入文件列表
	if (!parseArgs(argc, argv, iscompress,recursive, inputfiles, outputdir))//解析命令行参数
	{
		return 1;
	}
	int successcount = 0;
	int totalcount = inputfiles.size();
	for (const string& inputfile : inputfiles)//处理每个输入的文件或目录
	{
		if (processfileordirectory(inputfile, outputdir, iscompress,recursive))
		{
			successcount++;
		}
	}
	cout << "success" << endl;
	return (successcount == totalcount) ? 0 : 1;//返回0表示成功，1表示失败
}
